home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / help.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  7KB  |  302 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  * help.c: open a read-only window on the vim_help.txt file
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "option.h"
  17.  
  18.     void
  19. do_help(arg)
  20.     char_u        *arg;
  21. {
  22.     char_u    *fnamep;
  23.     FILE    *helpfd;            /* file descriptor of help file */
  24.     int        n;
  25.     WIN        *wp;
  26.     int        num_matches;
  27.     char_u    **matches;
  28.     int        need_free = FALSE;
  29.  
  30.     /*
  31.      * If an argument is given, check if there is a match for it.
  32.      */
  33.     if (*arg != NUL)
  34.     {
  35.         n = find_help_tags(arg, &num_matches, &matches);
  36.         if (num_matches == 0 || n == FAIL)
  37.         {
  38.             EMSG2("Sorry, no help for %s", arg);
  39.             return;
  40.         }
  41.  
  42.         /* The first file is the best match */
  43.         arg = strsave(matches[0]);
  44.         need_free = TRUE;
  45.         FreeWild(num_matches, matches);
  46.     }
  47.  
  48.     /*
  49.      * If there is already a help window open, use that one.
  50.      */
  51.     if (!curwin->w_buffer->b_help)
  52.     {
  53.         for (wp = firstwin; wp != NULL; wp = wp->w_next)
  54.             if (wp->w_buffer != NULL && wp->w_buffer->b_help)
  55.                 break;
  56.         if (wp != NULL && wp->w_buffer->b_nwindows > 0)
  57.             win_enter(wp, TRUE);
  58.         else
  59.         {
  60.             /*
  61.              * There is no help buffer yet.
  62.              * Try to open the file specified by the "helpfile" option.
  63.              */
  64.             fnamep = p_hf;
  65.             if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
  66.             {
  67. #if defined(MSDOS)
  68.             /*
  69.              * for MSDOS: try the DOS search path
  70.              */
  71.                 fnamep = searchpath("vim_help.txt");
  72.                 if (fnamep == NULL ||
  73.                             (helpfd = fopen((char *)fnamep, READBIN)) == NULL)
  74.                 {
  75.                     smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
  76.                     goto erret;
  77.                 }
  78. #else
  79.                 smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
  80.                 goto erret;
  81. #endif
  82.             }
  83.             fclose(helpfd);
  84.  
  85.             if (win_split(0, FALSE) == FAIL)
  86.                 goto erret;
  87.             
  88.             if (curwin->w_height < p_hh)
  89.                 win_setheight((int)p_hh);
  90.  
  91. #ifdef RIGHTLEFT
  92.             curwin->w_p_rl = 0;                /* help window is left-to-right */
  93. #endif
  94.             curwin->w_p_nu = 0;                /* no line numbers */
  95.  
  96.             /*
  97.              * open help file (do_ecmd() will set b_help flag, readfile() will
  98.              * set b_p_ro flag)
  99.              */
  100.             (void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);
  101.  
  102.             /* save the values of the options we change */
  103.             vim_free(help_save_isk);
  104.             help_save_isk = strsave(curbuf->b_p_isk);
  105.             help_save_ts = curbuf->b_p_ts;
  106.  
  107.             /* accept all chars for keywords, except ' ', '*', '"', '|' */
  108.             set_string_option((char_u *)"isk", -1,
  109.                                              (char_u *)"!-~,^*,^|,^\"", TRUE);
  110.             curbuf->b_p_ts = 8;
  111.             check_buf_options(curbuf);
  112.             (void)init_chartab();        /* needed because 'isk' changed */
  113.         }
  114.     }
  115.  
  116.     restart_edit = 0;        /* don't want insert mode in help file */
  117.  
  118.     stuffReadbuff((char_u *)":ta ");
  119.     if (arg != NULL && *arg != NUL)
  120.         stuffReadbuff(arg);
  121.     else
  122.         stuffReadbuff((char_u *)"vim_help.txt");        /* go to the index */
  123.     stuffcharReadbuff('\n');
  124.  
  125. erret:
  126.     if (need_free)
  127.         vim_free(arg);
  128. }
  129.  
  130. /*
  131.  * Return a heuristic indicating how well the given string matches.  The
  132.  * smaller the number, the better the match.  This is the order of priorities,
  133.  * from best match to worst match:
  134.  *        - Match with least alpha-numeric characters is better.
  135.  *        - Match with least total characters is better.
  136.  *        - Match towards the start is better.
  137.  * Assumption is made that the matched_string passed has already been found to
  138.  * match some string for which help is requested.  webb.
  139.  */
  140.     int
  141. help_heuristic(matched_string, offset)
  142.     char_u    *matched_string;
  143.     int        offset;                /* offset for match */
  144. {
  145.     int        num_letters;
  146.     char_u    *p;
  147.  
  148.     num_letters = 0;
  149.     for (p = matched_string; *p; p++)
  150.         if (isalnum(*p))
  151.             num_letters++;
  152.  
  153.     /*
  154.      * Multiply the number of letters by 100 to give it a much bigger
  155.      * weighting than the number of characters.
  156.      * If the match starts in the middle of a word, add 10000 to put it
  157.      * somewhere in the last half.
  158.      * If the match is more than 2 chars from the start, multiply by 200 to
  159.      * put it after matches at the start.
  160.      */
  161.     if (isalnum(matched_string[offset]) && offset > 0 &&
  162.                                           isalnum(matched_string[offset - 1]))
  163.         offset += 10000;
  164.     else if (offset > 2)
  165.         offset *= 200;
  166.     return (int)(100 * num_letters + STRLEN(matched_string) + offset);
  167. }
  168.  
  169. static int help_compare __ARGS((const void *s1, const void *s2));
  170.  
  171. /*
  172.  * Compare functions for qsort() below, that checks the help heuristics number
  173.  * that has been put after the tagname by find_tags().
  174.  */
  175.     static int
  176. help_compare(s1, s2)
  177.     const void    *s1;
  178.     const void    *s2;
  179. {
  180.     char    *p1;
  181.     char    *p2;
  182.  
  183.     p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
  184.     p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
  185.     return strcmp(p1, p2);
  186. }
  187.  
  188. /*
  189.  * Find all help tags matching "arg", sort them and return in matches[], with
  190.  * the number of matches in num_matches.
  191.  * We try first with case, and then ignoring case.  Then we try to choose the
  192.  * "best" match from the ones found.
  193.  */
  194.     int
  195. find_help_tags(arg, num_matches, matches)
  196.     char_u        *arg;
  197.     int            *num_matches;
  198.     char_u        ***matches;
  199. {
  200.     char_u    *s, *d;
  201.     regexp    *prog;
  202.     int        attempt;
  203.     int        retval = FAIL;
  204.  
  205.     reg_magic = p_magic;
  206.     d = IObuff;                /* assume IObuff is long enough! */
  207.  
  208.     /*
  209.      * Replace "|" with "bar", """ with "quote" and "*" with "star" to
  210.      * match the name of the tags for these commands.
  211.      * Replace "*" with ".*" and "?" with "." to match command line
  212.      * completion.
  213.      * Insert a backslash before '~', '$' and '.' to avoid their
  214.      * special meaning.
  215.      * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
  216.      * ":help i_^_CTRL-D" work.
  217.      * If tag starts with ', toss everything after a second '. Fixes
  218.      * CTRL-] on 'option'. (would include the trailing '.').
  219.      */
  220.     if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
  221.                                                STRCMP(arg, "]*") == 0)
  222.     {
  223.         if (*arg != '*')
  224.             *d++ = *arg;
  225.         STRCPY(d, "star");
  226.         d += 4;
  227.     }
  228.     else
  229.     {
  230.         for (s = arg; *s; ++s)
  231.         {
  232.             if (d - IObuff > IOSIZE - 10)        /* getting too long!? */
  233.                 break;
  234.             switch (*s)
  235.             {
  236.                 case '|':    STRCPY(d, "bar");
  237.                             d += 3;
  238.                             continue;
  239.                 case '\"':    STRCPY(d, "quote");
  240.                             d += 5;
  241.                             continue;
  242.                 case '*':    *d++ = '.';
  243.                             break;
  244.                             /* "?", ":?" and "?<CR>" are real tags */
  245.                 case '?':    if (arg[1] == NUL ||
  246.                                              STRCMP(arg, ":?") == 0 ||
  247.                                             STRCMP(arg, "?<CR>") == 0)
  248.                                 break;
  249.                             *d++ = '.';
  250.                             continue;
  251.                 case '$':
  252.                 case '.':
  253.                 case '~':    *d++ = '\\';
  254.                             break;
  255.             }
  256.             if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_'))    /* ^x */
  257.             {
  258.                 STRCPY(d, "CTRL-");
  259.                 d += 5;
  260.                 if (*s < ' ')
  261.                 {
  262.                     *d++ = *s + '@';
  263.                     continue;
  264.                 }
  265.                 ++s;
  266.             }
  267.             else if (*s == '^')            /* "^" or "CTRL-^" or "^_" */
  268.                 *d++ = '\\';
  269.             *d++ = *s;
  270.             if (*s == '\'' && s > arg && *arg == '\'')
  271.                 break;
  272.         }
  273.     }
  274.     *d = NUL;
  275.  
  276.     reg_ic = FALSE;
  277.     prog = vim_regcomp(IObuff);
  278.     if (prog == NULL)
  279.         return FAIL;
  280.  
  281.     /* First try to match with case, then without */
  282.     for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
  283.     {
  284.         *matches = (char_u **)"";
  285.         *num_matches = 0;
  286.         retval = find_tags(NULL, prog, num_matches, matches, TRUE);
  287.         if (retval == FAIL || *num_matches)
  288.             break;
  289.     }
  290.     vim_free(prog);
  291.  
  292. #ifdef HAVE_QSORT
  293.     /*
  294.      * Sort the matches found on the heuristic number that is after the
  295.      * tag name.  If there is no qsort, the output will be messy!
  296.      */
  297.     qsort((void *)*matches, (size_t)*num_matches,
  298.                                               sizeof(char_u *), help_compare);
  299. #endif
  300.     return OK;
  301. }
  302.